#ifndef COMBINATION_INDEX_GENERATOR_H
#define COMBINATION_INDEX_GENERATOR_H

#define BOOST_MPL_LIMIT_VECTOR_SIZE 30

#include <iostream>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/list_c.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/remove_if.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/erase.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/insert_range.hpp>

#include "main.h"

class CombinationPrinter
{
public:
	template<typename U>
	void operator()(U x)
	{
		std::cout << x << ' ';
	}
};

class CombinationsPrinter
{
public:
	template<typename U>
	void operator()(U x)
	{
		boost::mpl::for_each<U>(CombinationPrinter());
		std::cout << endl;
	}
};

template<Uint32 N, Uint32 i, Uint32 j>
class RemoveIndices
{
private:
	typedef boost::mpl::vector_c<Uint32>::type																			Combination;
	typedef typename boost::mpl::copy<boost::mpl::range_c<Uint32, 0, N>, boost::mpl::back_inserter<Combination>>::type	FilledCombination;
	typedef typename boost::mpl::find<FilledCombination, boost::mpl::integral_c<Uint32, i>>::type	Posi;
	typedef typename boost::mpl::erase<FilledCombination, Posi>::type								Result0;
	typedef typename boost::mpl::find<Result0, boost::mpl::integral_c<Uint32, j>>::type				Posj;

public:
	typedef typename boost::mpl::erase<Result0, Posj>::type							Result;
};

template<typename Combs, Uint32 N, Uint32 i, Uint32 j>
class CombinationsGenerator
{
private:
	typedef typename RemoveIndices<N, i, j>::Result						RemoveResult;

public:
	typedef typename CombinationsGenerator<typename boost::mpl::push_back<Combs, RemoveResult>::type, N, i, j-1>::Result		Result;
};

template<typename Combs, Uint32 N, Uint32 j>
class CombinationsGenerator<Combs, N, 0, j>
{
public:
	typedef typename Combs		Result;
};

template<typename Combs, Uint32 N, Uint32 i>
class CombinationsGenerator<Combs, N, i, 0>
{
private:
	typedef typename RemoveIndices<N, i, 0>::Result		RemoveResult;

public:
	typedef typename CombinationsGenerator<typename boost::mpl::push_back<Combs, RemoveResult>::type, N, i-1, i-2>::Result		Result;
};

template<typename Combs, Uint32 N>
class CombinationsGenerator<Combs, N, 1, 0>
{
private:
	typedef typename RemoveIndices<N, 1, 0>::Result		RemoveResult;

public:
	typedef typename boost::mpl::push_back<Combs, RemoveResult>::type		Result;
};

template<Uint32 N, Uint32 K>
class Combinations
{
public:
	typedef boost::mpl::vector_c<Uint32>																		Combination;
	typedef typename CombinationsGenerator<boost::mpl::vector_c<Combination>, N, N-1, N-2>::Result			Result;
};

#endif